/*****************************************************************
 *
 *  Copyright (c) 2020 KingSoft.com, Inc. All Rights Reserved
 *
 *****************************************************************/


/**
 * @file: ks3.h
 * @author: lihaibing(lihaibing@kingsoft.com)
 * @date: 2020-05-16 17:10:19
 * @brief: 
 *
 */

#ifndef _KS3_KS3_SDK_H_
#define _KS3_KS3_SDK_H_

#include <vector>
#include <string>
#include "curl_manager.h"
#include "Authorization.h"
#include "GeneratePresignedUrlRequest.h"

namespace ks3 {
namespace sdk {

static const auto KS3_SDK_VERSION = "v1.1.0";

static std::string DefaultUserAgent() {
    std::stringstream ss;
    ss << "ks3-cpp-sdk/" << KS3_SDK_VERSION << " (" << "C++" << __cplusplus << ")";
    return ss.str();
}

struct Owner
{
    std::string id;
    std::string display_name;
};

struct ListObjectsParam {
    std::string prefix;
    std::string delimiter;
    std::string marker;
    int max_keys = -1;
    std::string encoding_type;
};

struct ObjectContent
{
    std::string key;
    std::string etag;
    std::string type;
    int64_t size;
    std::string last_modified;
    std::string storage_class;
    Owner owner;
    std::string content_type;
    std::string content_md5;
};

struct ListObjectsResult
{
    std::vector<ObjectContent> contents;
    std::vector<std::string> common_prefixes;
    std::string bucket_name;
    std::string next_marker;
    bool is_truncated;
    std::string prefix;
    std::string marker;
    int max_keys;
    std::string delimiter;
    std::string encoding_type;
};


struct ClientContext {
    std::string bucket;
    std::string object_key;
    std::string accesskey;
    std::string secretkey;

    // for read
    int64_t start_offset;
    int64_t end_offset;

    // for multiple upload
    std::string uploadId;
    int partNum;

    std::map<std::string, std::string> headers;

    ClientContext() {
        start_offset = -1;
        end_offset = -1;

        partNum = -1;
    }
};

struct ReplicationInfo {
    // only replicate those objects which start with oneof following prefixes;
    // default is empty, replicate all objects
    std::vector<std::string> prefixes;
    std::string target_bucket;
    // true:replicate delete operation;
    // false: don't replicate delete operation;
    bool enable_delete_replication;

    ReplicationInfo() {
        enable_delete_replication = true;
    }
};

struct AccessControlPolicy {
    struct Grant
    {
        std::string grantee_id;
        std::string grantee_display_name;
        std::string grantee_uri;
        std::string grantee_email;
        std::string grantee_type;
        std::string permission;
    };

    Owner owner;
    std::vector<Grant> access_control_list;
};

class KS3Client {
public:
    explicit KS3Client(const std::string& host, int max_curl_sessions = 31, bool use_https = false, long socket_timeout = 30000, long connect_timeout = 10000);
    KS3Client(const std::string& host, const std::string& accessKeyId, const std::string& accessKeySecret, int max_curl_sessions = 31, bool use_https = false, long socket_timeout = 30000, long connect_timeout = 10000);
    virtual ~KS3Client();

    // should call this when program start up
    static int InitGlobalCurl() {
        return curl_global_init(CURL_GLOBAL_ALL);
    }

    static void DestroyGlobalCurl() {
        curl_global_cleanup();
    }

    int Init();
    int UploadObject(const ClientContext& ctx, const char* buffer, int buffer_size, KS3Response* response);
    int GetObject(const ClientContext& context, KS3Response* response);
    int GetObject(const ClientContext& context, KS3Response* response, const std::shared_ptr<std::iostream>& file_content);
    int DeleteObject(const ClientContext& context, KS3Response* response);
    int HeadObject(const ClientContext& context, KS3Response* response);

    // multiple part uploads
    int InitMultipartUpload(const ClientContext& ctx, KS3Response* response, std::string* uploadId);
    int UploadPart(const ClientContext& ctx, const char* buffer, int buffer_size, KS3Response* response,
                   std::string* etag);
    int CompleteMultipartUpload(const ClientContext& ctx, const std::map<int, std::string>& parts, KS3Response* response);
    int AbortMultipartUpload(const ClientContext& ctx, KS3Response* response);
    int ListParts(const ClientContext& ctx, std::map<int, std::string>* parts, KS3Response* response);

    // cross region replication
    int SetReplication(const ClientContext& ctx, const ReplicationInfo& crr_ctx, KS3Response* response);
    int GetReplication(const ClientContext& ctx, ReplicationInfo* crr_ctx, KS3Response* response);
    int DeleteReplication(const ClientContext& ctx, KS3Response* response);

    // acl
    int GetAcl(const ClientContext& ctx, AccessControlPolicy& policy, KS3Response* response);
    int SetAcl(const ClientContext& ctx, const AccessControlPolicy& policy, KS3Response* response);
    int SetCannedAcl(const ClientContext& context, const std::string& canned_acl, KS3Response* response);

    int ListObjects(const ClientContext& context, ListObjectsParam& param, ListObjectsResult* result, KS3Response* response);

    std::string GeneratePresignedUrl(const GeneratePresignedUrlRequest& request) const;

    void SetAuthorization(const std::string& accessKeyId, const std::string& secretKeyId);
    void SetUseHttps(bool use_https);

private:
    std::string userAgent_ = DefaultUserAgent();

    void BuildCommContext(const ClientContext& context, unsigned int* index, KS3Context* ctx);
    int Call(MethodType type, unsigned int index, KS3Context& ctx, KS3Response* result);

    DISALLOW_COPY_AND_ASSIGN(KS3Client);

    bool use_https_ = false;
    std::string host_;
    int max_curl_sessions_ = 31;
    std::vector<CurlManagerPtr> curl_managers_;
    long socket_timeout_ = 30000;
    long connect_timeout_ = 10000;
    std::shared_ptr<Authorization> authorization_;
};

}
}//namespace ks3

#endif
/* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */
